package com.tools.common.security.secret;

import com.tools.common.exception.DataGenerationException;
import com.tools.common.exception.ParseRuntimeException;
import com.tools.common.io.CharsetEnum;
import com.tools.common.object.Note;
import com.tools.common.security.id.IDKit;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * 加密解密的工具类
 * */
@Note("加密解密的工具类")
public final class SecretKit {

    private SecretKit() {}

    static {
        //初始化操作
        Security.addProvider(new BouncyCastleProvider());
    }

    /* ***********************************************************************************
     *
     *         非对称加密的密钥对
     *
     * **********************************************************************************
     * */

    @Note("获取长度为 32 的十六进制字符串作为 SM4 加密解密所需的密钥")
    public static String createSM4Key() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    @Note("获取长度为 32 的十六进制字符串（128 bit）作为 AES 加密解密所需的密钥")
    public static String createAES128BitKey() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    @Note("获取长度为 48 的十六进制字符串（192 bit）作为 AES 加密解密所需的密钥")
    public static String createAES192BitKey() {
        return IDKit.new16BinaryID(48);
    }

    @Note("获取长度为 64 的十六进制字符串（256 bit）作为 AES 加密解密所需的密钥")
    public static String createAES256BitKey() {
        return IDKit.new16BinaryID(64);
    }

    @Note("生成默认随机的 RSA 的 2048 个字符的密钥对")
    public static KeyPair createRSAKeyPair() {
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            return keyPairGenerator.generateKeyPair();
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("指定密钥字符个数，生成默认随机 RSA 的密钥对。keySize 的个数必须为 1024 的整数倍")
    public static KeyPair createRSAKeyPair(int keySize) {
        try {
            if(keySize <= 0) {
                return createRSAKeyPair();
            }
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            int mod = keySize % 1024;
            if(mod != 0) {
                keySize = (keySize - mod) + 1024;
            }
            keyPairGenerator.initialize(keySize);
            return keyPairGenerator.generateKeyPair();
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("将 RSA 公钥实例转为字符串输出")
    public static String rsaPublicKeyToString(PublicKey publicKey) {
        if(publicKey == null) throw new NullPointerException("RSA 公钥实例为 null");
        byte[] publicBytes = Base64.getEncoder().encode(publicKey.getEncoded());
        return new String(publicBytes);
    }


    @Note("将 RSA 私钥实例转为字符串输出")
    public static String rsaPrivateKeyToString(PrivateKey privateKey) {
        if(privateKey == null) throw new NullPointerException("入参私钥实例为 null");
        byte[] privateBytes = Base64.getEncoder().encode(privateKey.getEncoded());
        return new String(privateBytes);
    }


    @Note("将字符串公钥转为公钥对象")
    public static PublicKey toRSAPublicKey(String publicKey) {
        if(publicKey == null || publicKey.isEmpty()) throw new NullPointerException("RSA 字符串公钥为空");
        try {
            byte[] decode = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decode);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            return factory.generatePublic(keySpec);
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("将字符串公钥转为私钥对象")
    public static PrivateKey toRSAPrivateKey(String privateKey) {
        if(privateKey == null || privateKey.isEmpty()) throw new NullPointerException("RSA 字符串私钥为空");
        try {
            byte[] decode = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decode);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            return factory.generatePrivate(keySpec);
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("生成默认随机的 SM2 的密钥对")
    public static KeyPair createSM2KeyPair() {
        try {
            ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
            generator.initialize(sm2Spec);
            return generator.generateKeyPair();
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("将 SM2  BCEC 公钥实例转为字符串公钥")
    public static String sm2PublicKeyToString(BCECPublicKey publicKey) {
        if(publicKey == null) throw new NullPointerException("SM2 的 BCEC 公钥实例为 null");
        byte[] encoded = publicKey.getQ().getEncoded(true);
        return new String(Hex.encode(encoded));
    }


    @Note("将 SM2  BCEC 私钥实例转为字符串私钥")
    public static String sm2PrivateKeyToString(BCECPrivateKey privateKey) {
        if(privateKey == null) throw new NullPointerException("SM2 的 BCEC 私钥实例为 null");
        return privateKey.getD().toString(16);
    }


    @Note("将 SM2 字符串公钥转为 BCECPublicKey 公钥对象")
    public static BCECPublicKey toSM2PublicKey(BouncyCastleProvider provider, String publicKey) {
        try {
            // 获取 SM2 相关参数
            X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1");
            // 椭圆曲线参数规格
            ECParameterSpec parameterSpec = new ECParameterSpec(
                    parameters.getCurve(),
                    parameters.getG(),
                    parameters.getN(),
                    parameters.getH()
            );
            // 将公钥 hex 字符串转换为椭圆曲线对应的点
            ECPoint ecPoint = parameters.getCurve().decodePoint(Hex.decode(publicKey));
            // 获取椭圆曲线 key 生成器
            KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);
            // 将椭圆曲线点转为公钥 key 对象
            return  (BCECPublicKey) keyFactory.generatePublic(
                    new ECPublicKeySpec(ecPoint, parameterSpec)
            );
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }


    @Note("将 SM2 字符串私钥转为 BCECPrivateKey 私钥对象")
    public static BCECPrivateKey toSM2PrivateKey(BouncyCastleProvider provider, String privateKey) {
        try {
            // 获取 SM2 相关参数
            X9ECParameters parameters = GMNamedCurves.getByName("sm2p256v1");
            // 椭圆曲线参数规格
            ECParameterSpec parameterSpec = new ECParameterSpec(
                    parameters.getCurve(),
                    parameters.getG(),
                    parameters.getN(),
                    parameters.getH()
            );
            // 将私钥 hex 字符串转换为X值
            BigInteger bigInteger = new BigInteger(privateKey, 16);
            // 获取椭圆曲线 key 生成器
            KeyFactory keyFactory = KeyFactory.getInstance("EC", provider);
            // 将X值转为私钥 key 对象
            return (BCECPrivateKey) keyFactory.generatePrivate(
                    new ECPrivateKeySpec(bigInteger, parameterSpec)
            );
        } catch (Exception e) {
            throw new DataGenerationException(e);
        }
    }

    @Note("根据 secretKey 和指定字符集新建指定算法的 SecretKeySpec 实例")
    public static SecretKeySpec newSecretKeySpec(String secretKey, CharsetEnum charset, String algorithm) {
        return new SecretKeySpec(secretKey.getBytes(charset.getCharset()), algorithm);
    }

    @Note("根据 secretKey 和指定字符集新建 HmacSHA256 算法的 SecretKeySpec 实例")
    public static SecretKeySpec newHmacSHA256SecretKeySpec(String secretKey, CharsetEnum charset) {
        return new SecretKeySpec(secretKey.getBytes(charset.getCharset()), "HmacSHA256");
    }

    /* ***********************************************************************************
     *
     *         加密
     *
     * **********************************************************************************
     * */

    @Note("使用的对称加密算法 AES 和 UTF-8 字符集加密明文，返回加密后的密文")
    public static String encryptWithAES(String plaintext, String key) {
        return encryptWithAES(plaintext, key, CharsetEnum.UTF_8);
    }

    @Note("使用的对称加密算法 AES 和指定的字符集加密明文，返回加密后的密文")
    public static String encryptWithAES(String plaintext, String key, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkKey(key, "需要加密的密钥为空");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            Cipher cipher = Cipher.getInstance("AES");
            SecretKey secretKey = private_generateAESKey(key, charset);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(charset.getCharset()));
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }

    @Note("使用非对称加密算法 RSA 和 UTF-8 字符集加密明文，返回加密后的密文")
    public static String encryptWithRSA(String plaintext, String publicKey) {
        return encryptWithRSA(plaintext, toRSAPublicKey(publicKey), CharsetEnum.UTF_8);
    }

    @Note("使用非对称加密算法 RSA 和指定的字符集加密明文，返回加密后的密文")
    public static String encryptWithRSA(String plaintext, String publicKey, CharsetEnum charset) {
        return encryptWithRSA(plaintext, toRSAPublicKey(publicKey), charset);
    }


    @Note("使用非对称加密算法 RSA 和 UTF-8 字符集加密明文，返回加密后的密文")
    public static String encryptWithRSA(String plaintext, PublicKey publicKey) {
        return encryptWithRSA(plaintext, publicKey, CharsetEnum.UTF_8);
    }

    @Note("使用非对称加密算法 RSA 和指定的字符集加密明文，返回加密后的密文")
    public static String encryptWithRSA(String plaintext, PublicKey publicKey, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        if(publicKey == null) throw new NullPointerException("RSA 非对称算法所需的公钥为 null");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(charset.getCharset()));
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    @Note("使用哈希算法 SHA 和 UTF-8 字符集哈希加密明文，返回加密后的哈希值")
    public static String encryptWithHashSHA(String plaintext) {
        return encryptWithHashSHA(plaintext, CharsetEnum.UTF_8);
    }


    @Note("使用哈希算法 SHA 和指定的字符集哈希加密明文，返回加密后的哈希值")
    public static String encryptWithHashSHA(String plaintext, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            MessageDigest sha = MessageDigest.getInstance("SHA");
            byte[] digest = sha.digest(plaintext.getBytes(charset.getCharset()));
            return private_bytesToHex(digest);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    @Note("使用哈希算法 SHA-256 和 UTF-8 字符集哈希加密明文，返回加密后的哈希值")
    public static String encryptWithHashSHA256(String plaintext) {
        return encryptWithHashSHA256(plaintext, CharsetEnum.UTF_8);
    }


    @Note("使用哈希算法 SHA-256 和指定的字符集哈希加密明文，返回加密后的哈希值")
    public static String encryptWithHashSHA256(String plaintext, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(plaintext.getBytes(charset.getCharset()));
            return private_bytesToHex(hashBytes);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    @Note("使用 Base64 + UTF-8 字符集编码明文")
    public static String encodeBase64(String plaintext) {
        return encodeBase64(plaintext, CharsetEnum.UTF_8);
    }


    @Note("使用 Base64 + 指定的字符集编码明文")
    public static String encodeBase64(String plaintext, CharsetEnum charset) {
        private_checkText(plaintext, "需要编码的明文为空");
        private_checkCharset(charset, "编码明文所需的字符集不能为 null");
        byte[] plainBytes = plaintext.getBytes(charset.getCharset());
        return Base64.getEncoder().encodeToString(plainBytes);
    }


    @Note("使用 MD5 加密算法对明文进行加密，这种是加盐的加密算法")
    public static String encryptWithMD5(String plaintext, String salt, int addSaltCount) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkKey(salt, "MD5 加密所需的盐为空");
        if(addSaltCount <= 0) return encryptWithMD5(plaintext);
        StringBuilder builder = new StringBuilder(plaintext.length() + (salt.length() * addSaltCount));
        builder.append(salt).append(plaintext);
        for (int i = 0; i < addSaltCount; i++) {
            builder.append(salt);
        }
        return encryptWithMD5(builder.toString());
    }


    @Note("使用 MD5 加密算法对明文进行加密，这种是不加盐的加密算法")
    public static String encryptWithMD5(String plaintext) {
        private_checkText(plaintext, "需要加密的明文为空");
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            byte[] digest = messageDigest.digest(plaintext.getBytes());
            StringBuilder builder = new StringBuilder();
            for (byte b : digest) {
                int result = b & 0xff;
                String hexString = Integer.toHexString(result);
                if (hexString.length() < 2) {
                    builder.append("0");
                }
                builder.append(hexString);
            }
            return builder.toString();
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }

    @Note("使用 HmacSHA256 哈希算法 + UTF-8 字符集加密明文")
    public static String encryptWithHmacSHA256(String plaintext, String secretKey) {
        return encryptWithHmacSHA256(plaintext, secretKey, CharsetEnum.UTF_8);
    }

    @Note("使用 HmacSHA256 哈希算法 + UTF-8 字符集加密明文")
    public static String encryptWithHmacSHA256(String plaintext, SecretKeySpec secretKey) {
        return encryptWithHmacSHA256(plaintext, secretKey, CharsetEnum.UTF_8);
    }

    @Note("使用 HmacSHA256 哈希算法加密明文，并指定字符集编码")
    public static String encryptWithHmacSHA256(String plaintext, String secretKey, CharsetEnum charset) {
        SecretKeySpec secretKeySpec = newHmacSHA256SecretKeySpec(secretKey, charset);
        return encryptWithHmacSHA256(plaintext, secretKeySpec, charset);
    }

    @Note("使用 HmacSHA256 哈希算法加密明文，并指定字符集编码")
    public static String encryptWithHmacSHA256(String plaintext, SecretKeySpec secretKey, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkSecretKeySpec(secretKey, "使用 HmacSHA256 加密时，密钥不能为空");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(secretKey);
            byte[] hashData = mac.doFinal(plaintext.getBytes(charset.getCharset()));
            return Base64.getEncoder().encodeToString(hashData);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    @Note("使用恺撒加密算法将字符串每个字符进行偏移加密，最后返回加密后的密文")
    public static String encryptWithCaesar(String plaintext, int offset) {
        private_checkText(plaintext, "需要加密的明文为空");
        StringBuilder builder = new StringBuilder(plaintext.length());
        for (char c : plaintext.toCharArray()) {
            builder.append((char) ((int) c + offset));
        }
        return builder.toString();
    }

    @Note("使用 SM2 非对称加密算法加密明文，返回加密后的密文")
    public static String encryptWithSM2(String plaintext, String publicKey) {
        return encryptWithSM2(plaintext, toSM2PublicKey(new BouncyCastleProvider(), publicKey));
    }

    @Note("使用 SM2 非对称加密算法加密明文，返回加密后的密文")
    public static String encryptWithSM2(String plaintext, BCECPublicKey publicKey) {
        private_checkText(plaintext, "需要加密的明文为空");
        if(publicKey == null) throw new NullPointerException("加密所需的公钥为 null");
        try {
            // 获取 SM2 加密器
            Cipher cipher = Cipher.getInstance("SM2", new BouncyCastleProvider());
            // 初始化为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            //加密
            byte[] bytes = cipher.doFinal(plaintext.getBytes());
            //编码为 base64 的格式
            return Base64.getEncoder().encodeToString(bytes);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }



    @Note("使用 SM3 国密算法计算数据的摘要签名。这种跟 MD5 差不多，多次计算值一样，且不可逆的")
    public static String encryptWithSM3(String plaintext) {
        SM3Digest digest = new SM3Digest();
        byte[] textBytes = plaintext.getBytes(StandardCharsets.UTF_8);
        int textLength = textBytes.length;
        digest.update(textBytes, 0, textLength);
        byte[] result = new byte[textLength];
        digest.doFinal(result, 0);
        return Hex.toHexString(result);
    }


    @Note("使用对称加密算法国密 SM4 的 ECB 模式和 UTF-8 字符集加密明文，返回加密后的密文")
    public static String encryptWithSM4(String plaintext, String key) {
        return encryptWithSM4(plaintext, key, CharsetEnum.UTF_8);
    }

    @Note("使用对称加密算法国密 SM4 的 ECB 模式和指定的字符集加密明文，返回加密后的密文")
    public static String encryptWithSM4(String plaintext, String key, CharsetEnum charset) {
        private_checkText(plaintext, "需要加密的明文为空");
        private_checkKey(key, "加密明文所需的密钥不能为空");
        private_checkCharset(charset, "加密明文所需的字符集不能为 null");
        try {
            byte[] keyBytes = private_checkSM4Key(key);
            Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
            Key sm4Key = new SecretKeySpec(keyBytes, "SM4");
            cipher.init(Cipher.ENCRYPT_MODE, sm4Key);
            byte[] dataBytes = plaintext.getBytes(charset.getCharset());
            byte[] resultBytes = cipher.doFinal(dataBytes);
            return ByteUtils.toHexString(resultBytes);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    /* ***********************************************************************************
     *
     *         解密
     *
     * **********************************************************************************
     * */

    @Note("使用对称加密算法 AES 和 UTF-8 字符集解密密文，返回解密后的明文")
    public static String decryptWithAES(String ciphertext, String key) {
        return decryptWithAES(ciphertext, key, CharsetEnum.UTF_8);
    }

    @Note("使用对称加密算法 AES 和指定的字符集解密密文，返回解密后的明文")
    public static String decryptWithAES(String ciphertext, String key, CharsetEnum charset) {
        private_checkText(ciphertext, "需要解密的密文为空");
        private_checkKey(key, "需要解密的密钥为空");
        private_checkCharset(charset, "解密密文所需的字符集不能为 null");
        try {
            Cipher cipher = Cipher.getInstance("AES");
            SecretKey secretKey = private_generateAESKey(key, charset);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
            return new String(decryptedBytes, charset.getCharset());
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }

    @Note("使用非对称加密算法 RSA 和 UTF-8 字符集解密密文，返回解密后的明文")
    public static String decryptWithRSA(String ciphertext, String privateKey) {
        return decryptWithRSA(ciphertext, toRSAPrivateKey(privateKey), CharsetEnum.UTF_8);
    }

    @Note("使用非对称加密算法 RSA 和指定的字符集解密密文，返回解密后的明文")
    public static String decryptWithRSA(String ciphertext, String privateKey, CharsetEnum charset) {
        return decryptWithRSA(ciphertext, toRSAPrivateKey(privateKey), charset);
    }

    @Note("使用非对称加密算法 RSA 和 UTF-8 字符集解密密文，返回解密后的明文")
    public static String decryptWithRSA(String ciphertext, PrivateKey privateKey) {
        return decryptWithRSA(ciphertext, privateKey, CharsetEnum.UTF_8);
    }

    @Note("使用非对称加密算法 RSA 和指定的字符集解密密文，返回解密后的明文")
    public static String decryptWithRSA(String ciphertext, PrivateKey privateKey, CharsetEnum charset) {
        private_checkText(ciphertext, "需要解密的密文为空");
        if(privateKey == null) throw new NullPointerException("入参私钥实例为 null");
        private_checkCharset(charset, "解密密文所需的字符集不能为 null");
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
            return new String(decryptedBytes, charset.getCharset());
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    @Note("使用 Base64 + UTF-8 字符集解码密文")
    public static String decodeBase64(String base64Text) {
        return decodeBase64(base64Text, CharsetEnum.UTF_8);
    }


    @Note("使用 Base64 + 指定的字符集解码密文")
    public static String decodeBase64(String base64Text, CharsetEnum charset) {
        private_checkText(base64Text, "需要解码的密文为空");
        private_checkCharset(charset, "解码密文所需的字符集不能为 null");
        byte[] base64Bytes = Base64.getDecoder().decode(base64Text);
        return new String(base64Bytes, charset.getCharset());
    }


    @Note("使用恺撒算法解密密文，返回解密后的明文")
    public static String decryptWithCaesar(String ciphertext, int offset) {
        private_checkText(ciphertext, "需要解密的密文为空");
        StringBuilder builder = new StringBuilder(ciphertext.length());
        for (char c : ciphertext.toCharArray()) {
            builder.append(builder.append((char) ((int) c - offset)));
        }
        return builder.toString();
    }



    @Note("使用 SM2 非对称加密算法解密密文，返回解密后的明文")
    public static String decryptWithSM2(String ciphertext, String privateKey) {
        return decryptWithSM2(ciphertext, toSM2PrivateKey(new BouncyCastleProvider(), privateKey));
    }

    @Note("使用 SM2 非对称加密算法解密密文，返回解密后的明文")
    public static String decryptWithSM2(String ciphertext, BCECPrivateKey privateKey) {
        private_checkText(ciphertext, "需要解密的密文为空");
        if(privateKey == null) throw new NullPointerException("需要解密的私钥为 null");
        try {
            // 获取SM2加密器
            Cipher cipher = Cipher.getInstance("SM2", new BouncyCastleProvider());
            // 初始化为加密模式
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            //Base64 解码
            byte[] bytes = Base64.getDecoder().decode(ciphertext);
            //解密
            byte[] result = cipher.doFinal(bytes);
            return new String(result);
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }

    @Note("使用对称加密算法国密 SM4 的 ECB 模式和 UTF-8 字符集解密密文，返回解密后的明文")
    public static String decryptWithSM4(String ciphertext, String key) {
        return decryptWithSM4(ciphertext, key, CharsetEnum.UTF_8);
    }

    @Note("使用对称加密算法国密 SM4 的 ECB 模式和指定的字符集解密密文，返回解密后的明文")
    public static String decryptWithSM4(String ciphertext, String key, CharsetEnum charsetEnum) {
        private_checkText(ciphertext, "需要解密的密文为空");
        private_checkKey(key, "解密密文所需的密钥为空");
        private_checkCharset(charsetEnum, "解密密文所需的字符集不能为 null");
        try {
            byte[] keyBytes = private_checkSM4Key(key);
            Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding", BouncyCastleProvider.PROVIDER_NAME);
            Key sm4Key = new SecretKeySpec(keyBytes, "SM4");
            cipher.init(Cipher.DECRYPT_MODE, sm4Key);
            byte[] cipherBytes = ByteUtils.fromHexString(ciphertext);
            byte[] resultBytes = cipher.doFinal(cipherBytes);
            return new String(resultBytes, charsetEnum.getCharset());
        } catch (Exception e) {
            throw new ParseRuntimeException(e);
        }
    }


    /* ***********************************************************************************
     *
     *         私有逻辑
     *
     * **********************************************************************************
     * */


    @Note("根据字符串密钥生成密钥实例")
    private static SecretKey private_generateAESKey(String key, CharsetEnum charset) throws Exception {
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(128);
        byte[] passwordBytes = key.getBytes(charset.getCharset());
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] keyBytes = digest.digest(passwordBytes);
        return new SecretKeySpec(keyBytes, "AES");
    }

    @Note("将字节数组转为 16 进制字符串输出")
    private static String private_bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    @Note("普通密钥的非空校验")
    private static void private_checkKey(String key, String errMsg) {
        if(key == null || key.isEmpty()) {
            throw new NullPointerException(errMsg);
        }
    }

    @Note("SM4 密钥的非空校验，其必须是长度为 32 的十六进制字符")
    private static byte[] private_checkSM4Key(String key) {
        if(key == null || key.length() != 32) {
            throw new IllegalArgumentException("SM4 算法所需的密钥必须是长度为 32 的十六进制字符串");
        }
        try {
            return ByteUtils.fromHexString(key);
        } catch (Exception e) {
            throw new IllegalArgumentException("SM4 算法所需的密钥必须是长度为 32 的十六进制字符串");
        }
    }

    @Note("明文或者密文的非空校验")
    private static void private_checkText(String text, String errMsg) {
        if(text == null || text.isEmpty()) {
            throw new NullPointerException(errMsg);
        }
    }

    @Note("字符集的非空校验")
    private static void private_checkCharset(CharsetEnum charset, String errMsg) {
        if(charset == null) {
            throw new NullPointerException(errMsg);
        }
    }


    @Note("密钥对象的非空校验")
    private static void private_checkSecretKeySpec(SecretKeySpec secretKey, String errMsg) {
        if(secretKey == null) {
            throw new NullPointerException(errMsg);
        }
    }
}
